/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  TableCodecCommon.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2019/09/12
  *Desc		:  //用于主要说明此程序文件完成的主要功能
  *Relation :  
  *Others	:  //其他内容说明
  *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
  *History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
***************************************************************************************************/
#include "TableCodecCommon.h"
#include "common_util.h"
#include "TableCodec.h"
#include "portability.h"
#include <cstdlib>
#include <cmath>
namespace qb{
	namespace proto{
		class StringDelta{
			int		m_last;
			char*	m_buf;
			int		m_capcity;
			int		m_size;
			char	m_stack[256];		// 栈上的数据
		public:
			StringDelta(int capcity) :m_capcity(capcity), m_last(-1), m_size(0){
				m_buf = m_stack;
				m_capcity = _countof(m_stack);
				if (capcity > m_capcity)
				{
					m_buf = new char[capcity];
					m_capcity = capcity;
				}
			}
			~StringDelta()
			{
				if (m_buf != m_stack)
					delete[] m_buf;
			}
			const char* c_str()const{ return m_buf; }
			inline int size()const{ return m_size; }
			bool Init(const char* last,const char* cur,int len,bool check)
			{
				for (int i = 0; i < len; i++)
				{
					if (cur[i] != last[i])
					{
						if (cur[i] == '-')
							return false;
						AddDelta(i, cur[i]);
					}
					if (check && size() >= len / 2)
						return false;
				}
				if (check && size() >= len / 2)
					return false;
				return true;
			}
			static void Apply(char* buf,int capcity,const char* delta,int dlen)
			{
				// 将变化量delta应用到原始值buf上
				strinfo tokens[256] = { 0 };
				int ncount = StringUtil::SplitStringInfo(delta, dlen, "-", tokens, _countof(tokens));
				for (int i = 0; i + 1 < ncount; i += 2)
				{
					int pos = atoi(delta + tokens[i].pos);
					int len = tokens[i + 1].len;
					BASSERT(pos >= 0 && pos < capcity);
					BASSERT(pos + len <= capcity);
					const char* replace = delta + tokens[i + 1].pos;
					memcpy(buf + pos, replace, len);
				}
			}
		protected:
			void AddDelta(int i, char c)
			{
				if (m_last >= 0 && m_last + 1 != i)
				{
					AddString("-", 1);
					m_last = -1;
				}
				if (m_last < 0)
				{
					m_last = i;
					char buf[32] = { 0 };
					int len=MathUtil::itoa(i, buf, _countof(buf));
					AddString(buf, len);
					AddString("-", 1);
					//int len = FMTBUF(buf, "%d-", i);
					//AddString(buf, len);
					//AddNumber((unsigned int)i);
				}
				m_last = i;
				AddString(&c, 1);
			}
			void AddNumber(unsigned int i)
			{
				int len = 0;
				char buf[32] = { 0 };
				do 
				{
					buf[len++] = '0' + (unsigned char)(i % 10);
					i = i / 10;
				} while (i!=0);
				for (int k = 0; k+k+1 < len;k++)
				{
					std::swap(buf[k], buf[len - 1 - k]);
				}
				buf[len++] = '-';
				AddString(buf, len);
			}
			void AddString(const char* str, int len)
			{
				if (len + m_size >= m_capcity)
				{
					int cap = m_capcity + len + m_size+1;
					char* data = new char[cap];
					memcpy(data, m_buf, m_size);
					if (m_buf != m_stack && m_buf != NULL)
						delete[] m_buf;
					m_buf = data;
					m_capcity = cap;
				}
				memcpy(m_buf+m_size, str, len);
				m_size += len;
				m_buf[m_size] = 0;
			}
		};
		class StackVarAllocator : public VarAllocator{
			OnceAllocator		m_page;
			int					m_used;
			char				m_stack[512];
		public:
			StackVarAllocator() :m_used(0){ m_stack[0] = 0; }
			virtual ~StackVarAllocator(){}
			virtual void Reset(){ m_used = 0; m_stack[0] = 0; m_page.Reset(); }
			virtual char* AllocString(const char* str, int len)
			{
				if (m_used + len + 1 >= _countof(m_stack))
					return const_cast<char*>(m_page.AddString(str, len));
				char* dest = m_stack + m_used;
				memcpy(dest, str, len); m_used += len;
				m_stack[m_used++] = 0;
				return dest;
			}
		};
		class StopStringDelta{
			unsigned char	m_stack[256];		// 栈上的数据
			int				m_pos;				//
			const char*		m_start;			//
			int				m_len;				//
			StopCodec		m_codec;			//
		public:
			StopStringDelta(int capcity) :m_pos(-1),m_codec(m_stack, _countof(m_stack), true),
				m_start(NULL), m_len(0){}
			~StopStringDelta(){}
			const char* c_str()const{ return (const char*)m_stack; }
			inline int size()const{ return m_codec.GetPos(); }
			static void Apply(char* buf, int capcity, const char* delta, int dlen)
			{
				// 将变化量delta应用到buf上
				StopCodec codec((unsigned char*)delta, dlen, false);
				StackVarAllocator alloc;
				for (;;)
				{
					int pos = codec.GetInt(-1);
					if (pos < 0)
						break;
					VarValue val;
					if (codec.GetValue(kEncodeTypeStrRaw, val, &alloc) &&
						val.str.len > 0 && pos >= 0 && pos + val.str.len <= capcity)
					{
						memcpy(buf + pos, val.str.ptr, val.str.len);
					}
				}
			}
			bool Init(const char* last, const char* cur, int len, bool check)
			{
				for (int i = 0; i < len; i++)
				{
					if (cur[i] != last[i])
					{
						if (cur[i] == '-')
							return false;
						if (!AddDelta(i, cur + i))
							return false;
					}
					if (check && size() >= len / 2)
						return false;
				}
				if (check && size() >= len / 2)
					return false;
				return true;
			}
		protected:
			bool AddDelta(int i,const char* str)
			{
				if (m_pos >= 0 && m_pos + m_len == i && m_start)//衔接
				{
					m_len++;
				}
				else
				{
					if (m_pos >= 0 && m_len > 0 && m_start)
					{
						if (!AddString(m_pos, m_start, m_len))
							return false;
					}
					m_pos = i;
					m_start = str;
					m_len = 1;
				}
				return true;
			}
			bool AddString(int pos,const char* str, int len)
			{
				if (m_codec.AddValue(kEncodeTypeVarInt, VarValue(pos)) <= 0)
					return false;
				if (m_codec.AddValue(kEncodeTypeStrRaw, VarValue(str, len)) <= 0)
					return false;
				return true;				
			}
		};
		bool IncArray::add(int num)
		{
			if (m_count == 0)
			{
				m_base = num;
				m_delta = 0;
				m_count = 1;
				return true;
			}
			if (m_count == 1)
			{
				m_delta = num - m_base;
				m_count = 2;
				return true;
			}
			if (m_base + m_delta*m_count == num)
			{
				m_count++;
				return true;
			}
			return false;
		}


		typedef StringDelta StringDeltaClass;
		double ConvertPrecision::GetThreadhold(int width)
		{
			static const double delta[] = {
				0.0,				0.1, 
				0.01,				0.001, 
				0.0001,				0.00001, 
				0.000001,			0.0000001,
				0.00000001,			0.000000001, 
				0.0000000001,		0.00000000001,
				0.000000000001,		0.0000000000001,
				0.00000000000001,	0.000000000000001,
			};
			width = (width < 0 || width >= _countof(delta)) ? 6 : width;
			return delta[width];
		}
		const char* ConvertPrecision::GetFormat(int width)
		{
			static const char* fmts[] = {
				"%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f",
				"%.7f", "%.8f", "%.9f", "%.10f", "%.11f", "%.12f", "%.13f",
				"%.14f", "%.15f",
			};
			width = (width < 0 || width >= _countof(fmts)) ? 6 : width;
			return fmts[width];
		}
		VarValue::VarValue() :type(kExpectTypeNull), i(0){ str.ptr = NULL; str.len = 0; }
		unsigned char VarValue::GetStringOp(const VarValue& last,kEncodeType encodetype, int& size)const
		{
			// 现定于字符串
			// "Z0001892019FINPBB01SH"
			// "A0001892019FINPBB02SH"
			// delta:0-A-18-2
			// 测试当前字符串相对于上一个字符串,是取delta好,还是new好
			if (last.type != type)
				return kOpTypeNew;
			BASSERT(last.type == type && type == kExpectTypeString);
			// 为了简化,如果变化字符出现了'-',不考虑,返回new
			if (str.len != last.str.len)
				return kOpTypeNew;
			StringDeltaClass delta(str.len);
			if (delta.Init(last.str.ptr, str.ptr, str.len,true))
			{
				if (delta.size() < str.len)
				{
					size = StopCodec::CalcEncodeSize(encodetype, VarValue(delta.c_str(), delta.size()));
					return kOpTypeDelta;
				}
			}
			return kOpTypeNew;
		}
		void VarValue::AddDelta(const VarValue& delta, TableCodec* allocator)
		{
			if (type == kExpectTypeString)
			{
				BASSERT(allocator);
				BASSERT(delta.type == kExpectTypeString);
				BASSERT(str.ptr && str.len > 0);
				// "Z0001892019FINPBB01SH"
				// "Z0001892019FINPBB02SH"
				// delta:18-2
				char* ptr = allocator->AllocString(str.ptr, str.len);
				StringDeltaClass::Apply(ptr, str.len, delta.str.ptr, delta.str.len);
				str.ptr = ptr;//直接替换,旧指针统一由allocator掌管
				return;
			}
			BASSERT(type != kExpectTypeString && delta.type != kExpectTypeString);
			if (delta.IsDefault())
				return;
			if (IsDefault())
			{
				*this = delta;
				return;
			}
			switch (type)
			{
			case kExpectTypeDouble:d += delta.d; break;
			case kExpectTypeInt:i += delta.i; break;
			}
		}
		bool VarValue::IsDefault()const
		{
			if (type == kExpectTypeNull)return true;
			switch (type)
			{
			case kExpectTypeString:return str.ptr == NULL || str.len <= 0; break;
			case kExpectTypeDouble:return d == 0.0; break;
			case kExpectTypeInt:return i == (i64)0; break;
			}
			return false;
		}
		bool VarValue::EqualWith(const VarValue& vv)const
		{
			// kExpectTypeNull是默认值,所有类型的默认值都是0
			bool dft1 = IsDefault(), dft2 = vv.IsDefault();
			if (dft1 && dft2)return true;
			if (dft1 != dft2 && type!=kExpectTypeDouble)return false;
			switch (type)
			{
			case kExpectTypeString:
				return str.len == vv.str.len && str.ptr && vv.str.ptr && 
					memcmp(str.ptr, vv.str.ptr, str.len) == 0; 
				break;
			case kExpectTypeDouble:
				// 注:StopCodec::CalcEncodeSize和StopCodec::AddValue中
				// double转字符串,精度有效位为6位,因此此处比较值为0.000001
				return fabs(d - vv.d)<ConvertPrecision::GetThreadhold(); break;
			case kExpectTypeInt:return i == vv.i; break;
			}
			return false;
		}
		VarValue VarValue::Delta(const VarValue& last, TableCodec* allocator)const
		{
			// 计算当前值相对于上一个值last的变化量
			// 无符号类型,delta后变成对应的有符号类型
			// 字符串类型的delta返回默认值;
			if (type == kExpectTypeString)
			{
				BASSERT(allocator);
				BASSERT(last.type == kExpectTypeString);
				BASSERT(last.str.len == str.len);
				StringDeltaClass delta(str.len);
				delta.Init(last.str.ptr, str.ptr, str.len, false);
				const char* str=allocator->AllocString(delta.c_str(), delta.size());
				return VarValue(str, delta.size());
			}
			BASSERT(type != kExpectTypeString && last.type != kExpectTypeString);
			int idelta = 0;
			i64 i64delta = 0;
			kExpectType t = type;
			if (type != last.type)
			{
				if (type != kExpectTypeNull && last.type != kExpectTypeNull)
					return VarValue();
				t = type == kExpectTypeNull ? last.type : type;
			}
			switch (t)
			{
			case kExpectTypeDouble:
				return VarValue(d - last.d); 
				break;
			case kExpectTypeInt:
				return VarValue(i - last.i); 
				break;
			default:
				break;
			}
			return VarValue();
		}

	}//namespace base;
}//namespace ss;
